package cron

import (
	"encoding/json"
	"strconv"
	"sync"
	"time"
	"zimuzu_cvn_web_api/model"
	"zimuzu_cvn_web_api/pkg/drive/redis"
	"zimuzu_cvn_web_api/pkg/e"
	"zimuzu_cvn_web_api/pkg/errno"
	"zimuzu_cvn_web_api/service"
)

// 发放收益相关脚本

// 写日志
// Auth: hzp
func writerLog(i, success int) {
	e.Log("info", "【线程组", i, "】已完成 {", success, "} 条数据处理")
}

// 写进度
// Auth: hzp
func writerProcessLog(i, left, total int) {
	e.Log("info", "【线程组", i, "】进度为：", left, "/", total)
}

// 统计
// Auth: hzp
func MultipleStatisticalProfit() (err error) {

	// 是否重复统计
	e.Log("err", "开始进入多方结算统计程序")
	TagKey := e.MultipleStatisticalForTag
	if redis.Exists(TagKey) {
		e.Log("err", "重复统计异常")
		return errno.StatisticalAgainErr
	}
	redis.SetUnRe(TagKey, 1, -1)
	e.Log("err", "记录多方结算标识")

	// 需要处理的数据
	nodeFlows, err := model.GetMultipleNodeFlowsData()
	if err != nil {
		return err
	}
	e.Log("err", "统计的节点数为：", len(nodeFlows))

	// 将需要结算的用户写入到redis队列
	var totalNum int
	cacheKey := e.MultipleStatisticalForUser
	redis.UnDelete(cacheKey)
	if !redis.Exists(cacheKey) {
		for _, k := range nodeFlows {
			kk, err := json.Marshal(k)
			if err == nil {
				_, err = redis.LPush(cacheKey, string(kk))
			}
			if err == nil {
				totalNum++
			}
		}
	}
	e.Log("err", "将统计数据插入到队列完成，共计条目：", len(nodeFlows))

	// 获取此时此刻的cvn对应价格
	cvnPrice, _, err := service.GetCvntPrice()
	if err != nil {
		e.Log("err", "获取CVN的实时价格异常，不能执行结算:"+err.Error())
		return err
	}
	e.Log("err", "当日结算CVN价格为："+e.ParseString(cvnPrice, 4))

	// 提前将各线路流量汇总
	NetFlows, err := _getLastMultiple(1, cvnPrice)
	if err != nil {
		e.Log("err", "全网总流量数据获取失败:"+err.Error())
		return err
	} else {
		e.Log("err", "全网总流量数据:", NetFlows)
	}

	// TODO 多线程处理
	var wg sync.WaitGroup
	AgainNum := 10
	wg.Add(AgainNum)
	for ik := 1; ik <= AgainNum; ik++ {
		var success int
		go func(cacheKey string, totalNum, ss int) {
			defer wg.Done()
			var left int
			for ii := 0; ii < totalNum; ii++ {
				left, _ = redis.LLen(cacheKey)
				if left <= 0 {
					break
				}
				tempStr, _ := redis.LPop(cacheKey)
				err = _singleMultipleStatisticalProfit(tempStr, left, totalNum, ss, cvnPrice)
				if err == nil {
					success++
				} else {
					e.Log("err", err)
				}
			}
			writerLog(ss, success)
		}(cacheKey, totalNum, ik)
	}
	wg.Wait()
	e.Log("err", "线程组已全部完成")

	redis.UnDelete(TagKey)
	e.Log("err", "统计程序已全部完成")
	return nil
}

// 单节点处理
// uid  node_id  source  up
// Auth: hzp
type MultipleNodeInfo struct {
	NodeId int64   `json:"node_id" form:"node_id"`
	Source int     `json:"source" form:"source"`
	Type   int     `json:"type" form:"type"`
	Up     float64 `json:"up" form:"up"`
}

// 获取昨日结算流量和CVN数据
type LastMultiple struct {
	Up  float64 `json:"up" form:"up"`
	Cvn float64 `json:"cvn" form:"cvn"`
}

func _getLastMultiple(Source int, price float64) (res LastMultiple, err error) {

	cacheKey := e.MultipleStatisticalTag + ":last:cache"
	if redis.Exists(cacheKey) {
		// 直接取缓存数据
		rStr, err := redis.Get(cacheKey)
		if err != nil {
			return res, err
		}
		err = json.Unmarshal([]byte(rStr), &res)
		if err != nil {
			return res, err
		}
		return res, nil
	}

	// 昨日各线路 总节点流量数据
	res.Up, err = model.GetMultipleFlowsData(Source)
	if err != nil {
		return res, err
	}

	timeNowStr := time.Now().AddDate(0, 0, -1).Format(e.TimeFormatD)
	start := e.StrToTime(timeNowStr + " 00:00:00")
	end := e.StrToTime(timeNowStr + " 23:59:59")
	GBps, err := model.GetTheDateGbps(start, end, Source)
	if err != nil {
		GBps = 0.00
	}
	if Source == 1 {
		// 钛雾
		res.Cvn = e.TaiWuSettlementGbps * GBps / 30 / price
	} else if Source == 2 {
		// 网心
		res.Cvn = e.WxSettlementGbps * GBps / 30 / price
	} else {
		res.Cvn = 0.00
	}
	rB, err := json.Marshal(res)
	if err != nil {
		return res, err
	}
	redis.SetUnRe(cacheKey, string(rB), e.TimeFormatHour)
	return res, nil
}

// 单结点处理
// Auth: hzp
func _singleMultipleStatisticalProfit(tempStr string, left, totalNum, ss int, cvnPrice float64) (err error) {

	// 写进度
	writerProcessLog(ss, left, totalNum)

	// 解析数据
	var node MultipleNodeInfo
	err = json.Unmarshal([]byte(tempStr), &node)
	if err != nil {
		return err
	}

	e.Log("err", "正在统计节点=", node.NodeId, "&flows=", node.Up)

	// 初始化结果数据
	var res model.CheckMultipleNodeInfoResponse
	// 统计的时间 为昨日
	timeStatistical := time.Now().AddDate(0, 0, -1).Format(e.TimeFormatD)
	res.DateStr = timeStatistical
	res.NodeId = node.NodeId
	res.Type = node.Type
	res.Source = node.Source

	// 单节点 是否已重复统计
	err = model.CheckMgNodePaymentRecord(node.NodeId, node.Type, node.Source, timeStatistical)
	if err != nil {
		e.Log("err", "存在结算记录，不能重复结算")
		return err
	}

	// 单节点 质押情况、在线时长信息
	NodeInfo, err := model.GetSingleNodeInfoForRedis(node.NodeId)
	if err != nil {
		return err
	}
	var statisticalPre float64
	if NodeInfo.TokenStatus == 1 {
		// 未质押
		statisticalPre = e.ProfitUnPledgeStatistical
		res.PledgeStatus = 0
	} else if NodeInfo.TokenStatus == 2 || NodeInfo.TokenStatus == 20 {
		// 质押节点
		statisticalPre = 1.00
		res.PledgeStatus = 1
	} else {
		// 异常节点不做结算处理
		statisticalPre = 0.00
		res.PledgeStatus = 0
	}
	res.Uid = NodeInfo.Uid

	// 超级结点信息
	res.SuperNodeId, err = service.GetSuperNodeIdByUidFromCache(NodeInfo.Uid)
	if err != nil {
		return err
	}

	// 在线时长
	mapData := make(map[string]int, 0)
	err = json.Unmarshal([]byte(NodeInfo.HistoryOnlineTime), &mapData)
	if err != nil {
		res.OnlineTime = 0
	} else {
		if _, ok := mapData[timeStatistical]; !ok {
			res.OnlineTime = 0
		} else {
			res.OnlineTime = mapData[timeStatistical]
		}
	}
	SourceFlowsData, err := _getLastMultiple(node.Source, cvnPrice)
	if err != nil {
		return err
	}

	// 单节点 昨日流量数据
	res.Up = node.Up
	res.Status = 0
	res.SourceFlows = SourceFlowsData.Up
	res.SourceCvn = SourceFlowsData.Cvn
	res.SourceRate = e.Decimal(node.Up/SourceFlowsData.Up, 4)

	// TODO 在线时长 + 超级结点邀请码 + 质押
	if res.SuperNodeId > 0 {
		res.Status = 1
		res.Cvn = e.Decimal(res.SourceRate*SourceFlowsData.Cvn*statisticalPre, 2)
	} else {
		res.Status = 0
		// 没有超级结点不能结算收益
		res.Cvn = 0.00
	}

	// 在线时长不足12小时
	if res.OnlineTime < 12 {
		res.Cvn = 0.00
		res.Status = 0
	}

	// 解除质押的也没有收益
	if NodeInfo.TokenStatus == 3 {
		res.Cvn = 0.00
		res.Status = 0
	}

	// 如果单节点每天到达 10TB，该节点存在疑似刷流量问题
	if res.Up > 10*1024*1024*1024*1024 {
		res.Cvn = 0.00
		res.Status = 0
	}

	// 单节点 超级节点结算
	res.SuperCvn = 0.1 * res.Cvn
	e.Log("err", "节点数据准备完成。准备插入数据库,", res)

	// 开始写入node_payment 数据表
	err = model.AddMultipleMgNodePaymentRecord(res)
	if err != nil {
		e.Log("err", "写入结算记录失败：NodeId="+strconv.FormatInt(res.NodeId, 10)+" ,错误信息："+err.Error())
		return err
	}
	e.Log("err", "节点", node.NodeId, "数据写入完成")
	return nil
}

// 用户收益详情数据整理
func PaymentToProfitDetail(DateStr string) (err error) {

	e.Log("info", "开始写入Date=", DateStr, "数据")

	if DateStr == "" {
		DateStr = time.Now().AddDate(0, 0, -1).Format(e.TimeFormatD)
	}
	// 1、先校验是否已经统计过
	count, err := model.CheckTodayRecord(DateStr)
	if err != nil {
		return err
	}
	if count > 0 {
		e.Log("info", "已经存在记录，不能重新整理")
		return errno.ExistsRecord
	}
	paymentCount, err := model.CheckNodePaymentRecord(DateStr)
	if err != nil {
		return err
	}
	if paymentCount < 1 {
		e.Log("info", "没有结算记录，不能整理数据")
		return errno.RecordError
	}

	// 2、获取全网数据信息
	NetProfits, err := model.GetTheLastDateCdnProfit(DateStr)
	if err != nil {
		return err
	}

	// 3、汇集 指定日的 结算数据 写入列表数据
	err = model.AddUserProfitListDB(NetProfits, DateStr)
	if err != nil {
		return err
	}

	// 4、写入用户的待发放记录表 mg_user_payment
	err = model.AddMgUserPayment()
	if err != nil {
		e.Log("info", "写入待发放记录失败：", err.Error())
		return err
	}

	return nil
}
